Linux 堆溢出 Unsafe unlink
前文Linux 堆介绍了Linux的堆管理策略,这篇文章就介绍一下堆的溢出攻击方式之一:unsafe unlink.
概述
从前文我们可以了解到这个关键点:free一个chunk时,若与该chunk物理相邻的前/后堆块也为空闲状态,那么便会分别触发向后合并/向前合并,即unlink.
1 | fd = p->fd; |
攻击原理
示意图:chunkA->chunkB
ChunkA = prev_size | chunksize&flag | content
ChunkB = prev_size | chunkSize&flag | content
如果此时chunkA存在堆溢出漏洞,那么我们便可将精心构造的数据写入chunkB.
故攻击如下:
在chunkA中设置一个fakeChunk,并修改chunkB的flag指针为0(即前一个chunk处于空闲状态)。那么在我们free(chunkB)时,堆管理器就会将chunkB和我们的fakeChunk合并。从而触发一次任意的写操作:bk->fd = fd. 只要我们再让bk->fd等于任意函数的got地址或者其他后续会被调用的东西,fd设置为shellcode地址。那么之后该函数的具体实现就会被替换为我们的shellcode
这里其实有个小tips,那就是堆管理器如何根据当前堆块的指针确定上一个堆块的位置呢?答案是prev_size+pointer。
绕过glibc的安全防护:
因为存在double free以及内存腐败的问题,因此glibc推出了一系列的机制去检测free时该内存块是否正确。
- p->fd->bk = p && p->bk->fd = p
- b.prev_size = a.chunkSize
针对第一个问题,我们要做的就是找到一个指向堆的指针,然后让我们的fakeFD = pointer -3 sizeof(uint_32), fakeBK = pointer - 2 sizeof(uint_32).即,分别减去0xc和0x8,在64位的系统系统上分别为0x18和0x10.
这是为啥呢?
其实是因为取fd的地址实际就是找fd相对于p的偏移,取bk得地址实际就是找bk相对于p的偏移。那偏移怎么算?fakeFD和p中间隔了fake_chunksize(p == fake_prev_size),自己是p + 4。fakeBK与p隔了chunksize、fakefd,自己是p + 4 * 2.
第二个问题就简单多了,我们只需要覆盖chunkB的prev_size == fakeChunk的size,chunkB的flag第一位 == 0就搞定了
Vulunerable Demo & exploit
1 |
|
编译:
1 | gcc -o vulnerability_unsafelink vulnerability_unsafelink.c -g -w |
获得exit@got:结果为0x0804a020
1 | objdump -R vulnerability_unsafelink |
执行: